/*
 * Copyright (C) 2008 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2012 Sony Computer Entertainment Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "EditorClientManx.h"

#include "EditCommand.h"
#include "Editor.h"
#include "Frame.h"
#include "KeyboardEvent.h"
#include "NotImplemented.h"
#include "PlatformKeyboardEvent.h"
#include "Settings.h"
#include "WebViewPrivate.h"
#include <manx/KeyboardCodes.h>
#include <webkit/WebView.h>

// #define MYTRACE printf("+++ EditorClientManx::%s() +++ :L%d\n", __FUNCTION__, __LINE__);
#define MYTRACE


namespace WebCore {

EditorClientManx::EditorClientManx(WebKit::WebViewPrivate* webView)
    : m_webView(webView)
{
}

EditorClientManx::~EditorClientManx()
{
}

void EditorClientManx::pageDestroyed()
{
    MYTRACE;
    delete this;
}

void EditorClientManx::frameWillDetachPage(Frame*)
{
}

bool EditorClientManx::shouldDeleteRange(Range*)
{
    MYTRACE;
    notImplemented();
    return true;
}

bool EditorClientManx::shouldShowDeleteInterface(HTMLElement*)
{
    MYTRACE;
    notImplemented();
    return false;
}

bool EditorClientManx::smartInsertDeleteEnabled()
{
    MYTRACE;
    notImplemented();
    return true;
}

bool EditorClientManx::isSelectTrailingWhitespaceEnabled()
{
    MYTRACE;
    notImplemented();
    return true;
}

bool EditorClientManx::isContinuousSpellCheckingEnabled()
{
    MYTRACE;
    notImplemented();
    return false;
}

void EditorClientManx::toggleContinuousSpellChecking()
{
    MYTRACE;
    notImplemented();
}

bool EditorClientManx::isGrammarCheckingEnabled()
{
    MYTRACE;
    notImplemented();
    return false;
}

void EditorClientManx::toggleGrammarChecking()
{
    MYTRACE;
    notImplemented();
}

int EditorClientManx::spellCheckerDocumentTag()
{
    MYTRACE;
    notImplemented();
    return 0;
}

bool EditorClientManx::selectWordBeforeMenuEvent()
{
    MYTRACE;
    notImplemented();
    return true;
}

bool EditorClientManx::isEditable()
{
    MYTRACE;
    notImplemented();
    return false;
}

bool EditorClientManx::shouldBeginEditing(Range*)
{
    MYTRACE;
    notImplemented();
    return true;
}

bool EditorClientManx::shouldEndEditing(Range*)
{
    MYTRACE;
    notImplemented();
    return true;
}

bool EditorClientManx::shouldInsertNode(Node*, Range*,
    EditorInsertAction)
{
    MYTRACE;
    notImplemented();
    return true;
}

bool EditorClientManx::shouldInsertText(const String& str, Range*,
    EditorInsertAction)
{
    MYTRACE;
//    printf("   InsertText: {%s}\n", str.utf8().data());
    return true;
}

bool EditorClientManx::shouldApplyStyle(StylePropertySet*,
    Range*)
{
    MYTRACE;
    notImplemented();
    return true;
}

bool EditorClientManx::shouldMoveRangeAfterDelete(Range*, Range*)
{
    MYTRACE;
    notImplemented();
    return true;
}

bool EditorClientManx::shouldChangeSelectedRange(Range* fromRange, Range* toRange,
    EAffinity, bool stillSelecting)
{
    MYTRACE;
    notImplemented();
    return true;
}

void EditorClientManx::didBeginEditing()
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::didEndEditing()
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::willWriteSelectionToPasteboard(WebCore::Range*)
{
}

void EditorClientManx::getClientPasteboardDataForRange(WebCore::Range*, Vector<String>&, Vector<RefPtr<WebCore::SharedBuffer> >&)
{
}

void EditorClientManx::didWriteSelectionToPasteboard()
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::didSetSelectionTypesForPasteboard()
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::respondToChangedContents()
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::respondToChangedSelection(Frame*)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::registerUndoStep(PassRefPtr<UndoStep>)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::registerRedoStep(PassRefPtr<UndoStep>)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::clearUndoRedoOperations()
{
    MYTRACE;
    notImplemented();
}

bool EditorClientManx::canCopyCut(Frame*, bool defaultValue) const
{
    MYTRACE;
    notImplemented();
    return defaultValue;
}

bool EditorClientManx::canPaste(Frame*, bool defaultValue) const
{
    MYTRACE;
    notImplemented();
    return defaultValue;
}

bool EditorClientManx::canUndo() const
{
    MYTRACE;
    notImplemented();
    return false;
}

bool EditorClientManx::canRedo() const
{
    MYTRACE;
    notImplemented();
    return false;
}

void EditorClientManx::undo()
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::redo()
{
    MYTRACE;
    notImplemented();
}

static const unsigned CtrlKey = 1 << 0;
static const unsigned AltKey = 1 << 1;
static const unsigned ShiftKey = 1 << 2;

struct KeyDownEntry {
    unsigned virtualKey;
    unsigned modifiers;
    const char* name;
};

struct KeyPressEntry {
    unsigned charCode;
    unsigned modifiers;
    const char* name;
};

static const KeyDownEntry keyDownEntries[] = {
    { Manx::VK_LEFT,            0,                  "MoveLeft"                                    },
    { Manx::VK_LEFT,            ShiftKey,           "MoveLeftAndModifySelection"                  },
    { Manx::VK_LEFT,            CtrlKey,            "MoveWordLeft"                                },
    { Manx::VK_LEFT,            CtrlKey | ShiftKey, "MoveWordLeftAndModifySelection"              },
    { Manx::VK_RIGHT,           0,                  "MoveRight"                                   },
    { Manx::VK_RIGHT,           ShiftKey,           "MoveRightAndModifySelection"                 },
    { Manx::VK_RIGHT,           CtrlKey,            "MoveWordRight"                               },
    { Manx::VK_RIGHT,           CtrlKey | ShiftKey, "MoveWordRightAndModifySelection"             },
    { Manx::VK_UP,              0,                  "MoveUp"                                      },
    { Manx::VK_UP,              ShiftKey,           "MoveUpAndModifySelection"                    },
    { Manx::VK_PRIOR,           ShiftKey,           "MovePageUpAndModifySelection"                },
    { Manx::VK_DOWN,            0,                  "MoveDown"                                    },
    { Manx::VK_DOWN,            ShiftKey,           "MoveDownAndModifySelection"                  },
    { Manx::VK_NEXT,            ShiftKey,           "MovePageDownAndModifySelection"              },
    { Manx::VK_PRIOR,           0,                  "MovePageUp"                                  },
    { Manx::VK_NEXT,            0,                  "MovePageDown"                                },
    { Manx::VK_HOME,            0,                  "MoveToBeginningOfLine"                       },
    { Manx::VK_HOME,            ShiftKey,           "MoveToBeginningOfLineAndModifySelection"     },
    { Manx::VK_HOME,            CtrlKey,            "MoveToBeginningOfDocument"                   },
    { Manx::VK_HOME,            CtrlKey | ShiftKey, "MoveToBeginningOfDocumentAndModifySelection" },

    { Manx::VK_END,             0,                  "MoveToEndOfLine"                             },
    { Manx::VK_END,             ShiftKey,           "MoveToEndOfLineAndModifySelection"           },
    { Manx::VK_END,             CtrlKey,            "MoveToEndOfDocument"                         },
    { Manx::VK_END,             CtrlKey | ShiftKey, "MoveToEndOfDocumentAndModifySelection"       },

    { Manx::VK_BACK,            0,                  "DeleteBackward"                              },
    { Manx::VK_BACK,            ShiftKey,           "DeleteBackward"                              },
    { Manx::VK_DELETE,          0,                  "DeleteForward"                               },
    { Manx::VK_BACK,            CtrlKey,            "DeleteWordBackward"                          },
    { Manx::VK_DELETE,          CtrlKey,            "DeleteWordForward"                           },

    { 'B',                      CtrlKey,            "ToggleBold"                                  },
    { 'I',                      CtrlKey,            "ToggleItalic"                                },

    { Manx::VK_ESCAPE,          0,                  "Cancel"                                      },
    { Manx::VK_OEM_PERIOD,      CtrlKey,            "Cancel"                                      },
    { Manx::VK_TAB,             0,                  "InsertTab"                                   },
    { Manx::VK_TAB,             ShiftKey,           "InsertBacktab"                               },
    { Manx::VK_RETURN,          0,                  "InsertNewline"                               },
    { Manx::VK_RETURN,          CtrlKey,            "InsertNewline"                               },
    { Manx::VK_RETURN,          AltKey,             "InsertNewline"                               },
    { Manx::VK_RETURN,          AltKey | ShiftKey,  "InsertNewline"                               },

    { Manx::VKX_PS_CARET_LEFT,  0,                  "MoveLeft"                                    },
    { Manx::VKX_PS_CARET_LEFT,  ShiftKey,           "MoveLeftAndModifySelection"                  },
    { Manx::VKX_PS_CARET_RIGHT, 0,                  "MoveRight"                                   },
    { Manx::VKX_PS_CARET_RIGHT, ShiftKey,           "MoveRightAndModifySelection"                 },
    { Manx::VKX_PS_CARET_UP,    0,                  "MoveUp"                                      },
    { Manx::VKX_PS_CARET_UP,    ShiftKey,           "MoveUpAndModifySelection"                    },
    { Manx::VKX_PS_CARET_DOWN,  0,                  "MoveDown"                                    },
    { Manx::VKX_PS_CARET_DOWN,  ShiftKey,           "MoveDownAndModifySelection"                  },
};

static const KeyPressEntry keyPressEntries[] = {
    { '\t',   0,                  "InsertTab"                                   },
    { '\t',   ShiftKey,           "InsertBacktab"                               },
    { '\r',   0,                  "InsertNewline"                               },
    { '\r',   CtrlKey,            "InsertNewline"                               },
    { '\r',   AltKey,             "InsertNewline"                               },
    { '\r',   AltKey | ShiftKey,  "InsertNewline"                               },
};

#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(array[0]))

static const char* interpretEditorCommandKeyEvent(const KeyboardEvent* evt)
{
    MYTRACE;
    ASSERT(evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent);

    static HashMap<int, const char*>* keyDownCommandsMap = 0;
    static HashMap<int, const char*>* keyPressCommandsMap = 0;

    if (!keyDownCommandsMap) {
        keyDownCommandsMap = new HashMap<int, const char*>;
        keyPressCommandsMap = new HashMap<int, const char*>;

        for (unsigned i = 0; i < NUM_ELEMENTS(keyDownEntries); i++)
            keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);

        for (unsigned i = 0; i < NUM_ELEMENTS(keyPressEntries); i++)
            keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
    }

    unsigned modifiers = 0;
    if (evt->shiftKey())
        modifiers |= ShiftKey;
    if (evt->altKey())
        modifiers |= AltKey;
    if (evt->ctrlKey())
        modifiers |= CtrlKey;

    if (evt->type() == eventNames().keydownEvent) {
        int mapKey = modifiers << 16 | evt->keyCode();
        return mapKey ? keyDownCommandsMap->get(mapKey) : 0;
    }

    int mapKey = modifiers << 16 | evt->charCode();
    return mapKey ? keyPressCommandsMap->get(mapKey) : 0;
}

void EditorClientManx::handleKeyboardEvent(KeyboardEvent* event)
{
    MYTRACE;
    Node* node = event->target()->toNode();
    ASSERT(node);
    Frame* frame = node->document()->frame();
    ASSERT(frame);

    const PlatformKeyboardEvent* platformEvent = event->keyEvent();
    if (!platformEvent)
        return;

    // Don't allow editor commands or text insertion for nodes that
    // cannot edit, unless we are in caret mode.
    if (!frame->editor().canEdit() && !(frame->settings() && frame->settings()->caretBrowsingEnabled()))
        return;

    const char* editorCommandString = interpretEditorCommandKeyEvent(event);
    if (editorCommandString) {
        Editor::Command command = frame->editor().command(editorCommandString);

        // On editor commands from key down events, we only want to let the event bubble up to
        // the DOM if it inserts text. If it doesn't insert text (e.g. Tab that changes focus)
        // we just want WebKit to handle it immediately without a DOM event.
        if (platformEvent->type() == PlatformKeyboardEvent::RawKeyDown) {
            if (!command.isTextInsertion() && command.execute(event))
                event->setDefaultHandled();

            return;
        } 
        if (command.execute(event)) {
            event->setDefaultHandled();
            return;
        }
    }

    // This is just a normal text insertion, so wait to execute the insertion
    // until a keypress event happens. This will ensure that the insertion will not
    // be reflected in the contents of the field until the keyup DOM event.
    if (event->type() == eventNames().keypressEvent) {
        // Don't insert null or control characters as they can result in unexpected behaviour
        if (event->charCode() < ' ')
            return;

        // Don't insert anything if a modifier is pressed
        if (platformEvent->ctrlKey() || platformEvent->altKey())
            return;
        if (frame->editor().insertText(platformEvent->text(), event))
            event->setDefaultHandled();
    }
}

void EditorClientManx::handleInputMethodKeydown(KeyboardEvent* event)
{
    MYTRACE;
// NOTE: we don't currently need to handle this. When key events occur,
// both this method and handleKeyboardEvent get a chance at handling them.
// We might use this method later on for IME-specific handling.
}

void EditorClientManx::textFieldDidBeginEditing(Element*)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::textFieldDidEndEditing(Element*)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::textDidChangeInTextField(Element*)
{
    MYTRACE;
    notImplemented();
}

bool EditorClientManx::doTextFieldCommandFromEvent(Element*, KeyboardEvent*)
{
    MYTRACE;
    return false;
}

void EditorClientManx::textWillBeDeletedInTextField(Element*)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::textDidChangeInTextArea(Element*)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::ignoreWordInSpellDocument(const String&)
{
    MYTRACE;
    notImplemented();
}

bool EditorClientManx::shouldEraseMarkersAfterChangeSelection(TextCheckingType) const
{
    return true;
}

void EditorClientManx::learnWord(const String&)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::checkSpellingOfString(const UChar*, int length, int* misspellingLocation, int* misspellingLength)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::checkGrammarOfString(const UChar*, int length, Vector<GrammarDetail>&, int* badGrammarLocation, int* badGrammarLength)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::updateSpellingUIWithGrammarString(const String&, const GrammarDetail& detail)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::updateSpellingUIWithMisspelledWord(const String&)
{
    MYTRACE;
    notImplemented();
}

void EditorClientManx::showSpellingUI(bool show)
{
    MYTRACE;
    notImplemented();
}

bool EditorClientManx::spellingUIIsShowing()
{
    MYTRACE;
    notImplemented();
    return false;
}

void EditorClientManx::getGuessesForWord(const String&, const String&, Vector<String>&)
{
    MYTRACE;
    notImplemented();
}

String EditorClientManx::getAutoCorrectSuggestionForMisspelledWord(const WTF::String&)
{
    MYTRACE;
    notImplemented();
    return String();
}

void EditorClientManx::willSetInputMethodState()
{
    MYTRACE;
    m_webView->confirmComposition();
}

void EditorClientManx::setInputMethodState(bool enabled)
{
    m_webView->client().setInputMethodState(enabled);
}

void EditorClientManx::requestCheckingOfString(PassRefPtr<TextCheckingRequest>)
{
}    

}
